package com.google.code.hibernate.rest.representation.json;
import static com.google.code.hibernate.rest.representation.json.JSONRepresentor.HREF;

import java.io.Serializable;

import com.google.code.hibernate.rest.EntityManager;
import com.google.code.hibernate.rest.representation.CategoryList;
import com.google.code.hibernate.rest.representation.EntityList;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.ConverterLookup;
import com.thoughtworks.xstream.converters.DataHolder;
import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
import com.thoughtworks.xstream.core.TreeMarshaller;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;

/**
 * 
 * @author wangzijian
 * 
 */
class EntityJSONMarshaller extends TreeMarshaller {

	private final ReflectionConverter reflectionConverter;
	private final EntityManager entityManager;
	private boolean isRoot = true;
	
	public EntityJSONMarshaller(
			HierarchicalStreamWriter writer, 
			ConverterLookup converterLookup, 
			Mapper mapper,
			EntityManager entityManager) {
		super(writer, converterLookup, mapper);
		this.reflectionConverter = (ReflectionConverter) converterLookup.lookupConverterForType(Representable.class);
		this.entityManager = entityManager;
	}
	
	@Override
	public void start(Object item, DataHolder dataHolder) {
		if (!(item instanceof EntityList)) {
			super.start(item, dataHolder);
			return;
		}
		String name = ((EntityList<?>) item).getWrapperName();
		writer.startNode(name);
		convertAnother(item);
		writer.endNode();
	}
	
	@Override
	public void convert(Object item, Converter converter) {
		if (isRoot) {
			isRoot = false;
			convertRoot(item);
			return;
		}
		if (entityManager.isEntity(item.getClass())) {
			addReference(item);
			return;
		}
		super.convert(item, converter);
	}

	private void convertRoot(Object item) {
		if (item instanceof EntityList) {
			for (Object each : (EntityList<?>) item) {
				writer.startNode(getMapper().serializedClass(each.getClass()));
				addReference(each);
				super.convert(each, reflectionConverter);
				writer.endNode();
			}
		} else if (item instanceof CategoryList) {
			for (String each : (CategoryList) item) {
				String entityName = (String) each;
				writer.startNode("Entity");
				writer.addAttribute(HREF, baseURL() + entityName);
				writer.endNode();
			}
		} else {
			super.convert(item, reflectionConverter);
		}
	}

	private void addReference(Object item) {
		String entityName = getMapper().serializedClass(item.getClass());
		Serializable id = entityManager.getId(item);
		writer.addAttribute(HREF, baseURL() + entityName + "/" + id);
	}

	private String baseURL() {
		return JSONRepresentor.getBaseURL();
	}

	private interface Representable {

	}
}
